//
//   Copyright 2011 Gynvael Coldwind & Mateusz "j00ru" Jurczyk
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//
#include <math.h>
#include "gynvael.h"
#include "global.h"
#include "j00ru.h"
#include "music.h"

SPRITE_STURCT g_sprites[100]; // Global

int g_AboutPage;

float mmx, mmy;
float target_vx, target_vy;
int   target_floor_x, target_floor_y;
bool lmb, rmb; // mouse buttons

void gynvael_RecalcTarget(float m_x, float m_y)
{
  // Get active player
  PLAYER_STRUCT *ap = &g_GameState.players[g_GameState.activePlayer];

  float vx = m_x - ap->x;
  float vy = m_y - ap->y;

  float vsz = sqrtf(vx * vx + vy * vy);
  if(vsz == 0.0f)
    vsz = 1.0f;

  vx /= vsz;
  vy /= vsz;

  target_vx = vx;
  target_vy = vy;

  // Calc target floor
  target_floor_x = int(m_x);
  target_floor_y = int(m_y);
}

void draw_quad(float x, float y, float w, float h, float r, float g, float b);
void draw_quad_coords(float x, float y, float mw, float mh, float w, float h);

void gynvael_DrawHole(float dx, float dy, uint32_t surround);

void gynvael_DrawTile_Door(float dx, float dy, TILE_STRUCT *tile, bool vert);
void gynvael_DrawTile_EndPoint(float dx, float dy, TILE_STRUCT *tile);
void gynvael_DrawTile_ExitPoint(float dx, float dy, TILE_STRUCT *tile);
void gynvael_DrawTile_StdButton(float dx, float dy, TILE_STRUCT *tile);
void gynvael_DrawTile_ToggleButton(float dx, float dy, TILE_STRUCT *tile);
void gynvael_DrawTile_Teleport(float dx, float dy, TILE_STRUCT *tile);

void gynvael_DrawAnim(float dx, float dy, float w, float h, int anim_id, int start, int last, float delay);

void gynvael_DrawAnimCenter(float dx, float dy, float w, float h, int anim_id, int start, int last, float delay)
{
  gynvael_DrawAnim(dx - w/2.0f, dy - h/2.0f, w, h, anim_id, start, last, delay);
}

struct OBJMAP_TILE
{
  TILE_STRUCT *tile;
  int x, y;
};

// Check collision vs:
void gynvael_ObjMapGetTiles(float x, float y, float w, float h, int map_id, OBJMAP_TILE tiles[4])
{
  float wh = w / 2.0f;
  float hh = h / 2.0f;
  float x0 = x - wh;
  float x1 = x + wh;
  float y0 = y - hh;
  float y1 = y + hh;

  memset(tiles, 0, sizeof(OBJMAP_TILE) * 4);

  int ixy[4][2] = {
    { int(x0), int(y0) },
    { int(x1), int(y0) },
    { int(x0), int(y1) },
    { int(x1), int(y1) }
  };

  MAP_STRUCT *map = g_GameState.currentLevel->maps[map_id];
  int map_width  = map->width;
  int map_height = map->height;

  for(int i = 0; i < 4; i++)
  {
    int ix = ixy[i][0];
    int iy = ixy[i][1];
    if(ix < 0 || iy < 0 || ix >= map_width || iy >= map_height)
      continue;

    tiles[i].tile = &map->tiles[iy][ix];
    tiles[i].x = ix;
    tiles[i].y = iy;
  }  
}

// return true on colide
bool gynvael_HandleFireballMap(float x, float y, float w, float h, int map_id)
{
   bool was_collision = false;
  
   OBJMAP_TILE c[4];
   gynvael_ObjMapGetTiles(x, y, w, h, map_id, c);

   for(int i = 0; i < 4; i++)
   {
     // Edge of map? Remove.
     if(c[i].tile == NULL)
     {
       was_collision = true;
       continue;
     }

     // Was wall? Destroy wall
     if(c[i].tile->tileType == TILE_WALL)
     {
       was_collision = true;
       c[i].tile->tileType = TILE_HOLE;
       continue;
     }

     // Closed door?
     if(c[i].tile->tileType == TILE_DOOR_H || c[i].tile->tileType == TILE_DOOR_V)
     {
       // Are the doors closed?
       int id = c[i].tile->params[0];
       if(!g_ObjectParams[id][0])
       {
         was_collision = true;
         break;
       }
     }     
     
     // That's it.
   }

   if(was_collision && map_id == g_GameState.currentLevel->activeMap)
      music_playSound(SND_HIT);

   return was_collision;
}

// return true on colide
bool gynvael_HandleBulletMap(float x, float y, float w, float h, int map_id)
{
   bool was_collision = false;
  
   OBJMAP_TILE c[4];
   gynvael_ObjMapGetTiles(x, y, w, h, map_id, c);

   for(int i = 0; i < 4; i++)
   {
     // Edge of map? Remove.
     if(c[i].tile == NULL)
     {
       was_collision = true;
       continue;
     }

     // Was wall?
     if(c[i].tile->tileType == TILE_WALL)
     {
       was_collision = true;
       continue;
     }

     // Closed door?
     if(c[i].tile->tileType == TILE_DOOR_H || c[i].tile->tileType == TILE_DOOR_V)
     {
       // Are the doors closed?
       int id = c[i].tile->params[0];
       if(!g_ObjectParams[id][0])
       {
         was_collision = true;
         break;
       }
       continue;
     }

     // Was it a triggering button ?
     if(c[i].tile->tileType == TILE_TOGGLE_BUTTON)
     {
       // Toggle!
       int id = c[i].tile->params[0];
       g_ObjectParams[id][0] = !g_ObjectParams[id][0];
       was_collision = true;

       if(map_id == g_GameState.currentLevel->activeMap)
         music_playSound(SND_DOOR);

       break; // This is all we really cared about.
     }
     
     // That's it.
   }

   if(was_collision && map_id == g_GameState.currentLevel->activeMap)
      music_playSound(SND_HIT);

   return was_collision;
}

void gynvael_UpdateObjects()
{
  size_t obj_size = g_Objects.size();
  size_t n = 0;
  for(n = 0; n < obj_size; n++)
  {
    OBJECT_STRUCT *obj = g_Objects[n];

    // TODO: check colision
    bool rm_obj = false;

    
    switch(obj->type)
    {
      case OBJECT_TYPE_SPELL_BULLET:
        obj->x += obj->u.spellBullet.vx * ratio;
        obj->y += obj->u.spellBullet.vy * ratio;
        rm_obj = gynvael_HandleBulletMap(obj->x, obj->y, obj->w * 0.5f, obj->h * 0.5f, obj->map_id);
        break;

      case OBJECT_TYPE_SPELL_FIREBALL:
        obj->x += obj->u.spellFireball.vx * ratio;
        obj->y += obj->u.spellFireball.vy * ratio;
        rm_obj = gynvael_HandleFireballMap(obj->x, obj->y, obj->w * 0.5f, obj->h * 0.5f, obj->map_id);            
        break;

      default:
        continue;
    }

    if(rm_obj)
    {
      if(n+1 < obj_size) // not last element
      {
        OBJECT_STRUCT *tmp = g_Objects[obj_size - 1];
        g_Objects[obj_size - 1] = obj;
        g_Objects[n] = tmp;
      }

      obj_size--;
      g_Objects.pop_back();

      delete obj;
    }
  } 
}

void
gynvael_LoadTextures()
{
  textures[TEX_BACKGROUND]  = make_texture("gfx/bg.png"); // render 4:3
  textures[TEX_TILE_FLOOR]  = make_texture("gfx/Kystal_BackTile025.png");
  textures[TEX_TILE_BRIDGE] = make_texture("gfx/bridge.png");
  textures[TEX_PLAYER_TEMP] = make_texture("gfx/player_nvm.png");
  textures[TEX_PLAYER_PTR]  = make_texture("gfx/player_ptr.png");
  textures[TEX_MOUSE_PTR]   = make_texture("gfx/Krystal_Cursor.png");
  textures[TEX_VXLOGO]      = make_texture("gfx/vxlogo.png");
  textures[TEX_LOSE_SCREEN] = make_texture("gfx/lose_screen.png");
  textures[TEX_ABOUT]       = make_texture("gfx/about.png");
  textures[TEX_LVL_COMPLETED]  = make_texture("gfx/level_completed.png");
  textures[TEX_GAME_COMPLETED] = make_texture("gfx/game_completed.png");

  textures[TEX_BG2] = make_texture("gfx/CC_game_background002.png");
  textures[TEX_BG3] = make_texture("gfx/CC_game_background003.png");
  textures[TEX_BG6] = make_texture("gfx/CC_game_background006.png");
  textures[TEX_BEFORE_LVL]  = make_texture("gfx/before_lvl.png");

  textures[TEX_TUT_FIRE]    = make_texture("gfx/tut_fire.png");  
  textures[TEX_TUT_TRIGGER] = make_texture("gfx/tut_trigger.png");  
  textures[TEX_TUT_TELE]    = make_texture("gfx/tut_tele.png");  
  textures[TEX_TUT_BRIDGE]  = make_texture("gfx/tut_bridge.png");  
  textures[TEX_TUT_STORY]   = make_texture("gfx/tut_story.png");  
  textures[TEX_TUT_EXIT]   = make_texture("gfx/tut_endlvl.png");  


  textures[TEX_PLAY_BTN]    = make_texture("gfx/play_btn.png");  
  textures[TEX_ABOUT_BTN]   = make_texture("gfx/about_btn.png");  
  textures[TEX_EXIT_BTN]    = make_texture("gfx/exit_btn.png");  
  textures[TEX_ARROW]       = make_texture("gfx/arrow.png");  
  textures[TEX_TARGET_PTR]  = make_texture("gfx/targeter.png");

  make_sprite(SPRITE_TOGGLE_BUTTON_OFF, "gfx/Krystal_OB_06b.png", 48, 48);
  make_sprite(SPRITE_TOGGLE_BUTTON_ON,  "gfx/Krystal_OB_06a.png", 48, 48);
  make_sprite(SPRITE_END_LEVEL,         "gfx/Krystal_OB_10a.png", 48, 48);
  make_sprite(SPRITE_EXIT_LEVEL,        "gfx/Krystal_OB_10b.png", 48, 48);

  make_sprite(SPRITE_SPELL_BULLET,      "gfx/bullet.png", 32, 32);
  make_sprite(SPRITE_SPELL_FIREBALL,    "gfx/fireball.png", 32, 32);
  make_sprite(SPRITE_PLAYER,            "gfx/player.png", 64, 64);

  make_sprite(SPRITE_TELEPORT_IN,         "gfx/Krystal_OB_08a.png", 48, 48);
  make_sprite(SPRITE_TELEPORT_OUT,        "gfx/Krystal_OB_08b.png", 48, 48);

  make_sprite(SPRITE_MIX1,              "gfx/sprites_mix1.png", 64, 64);

  fonts[0] = fnt_CreateFont("gfx/font1_32_32.png", 
      "ABCDEFGHIJKLMNOPQRST"
      "UVWXYZ#>0123456789()", 32, 32);

  fonts[1] = fnt_CreateFont("gfx/font2_24_23.png", 
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ#"
      "0123456789().,->[_]        ", 24, 23);  

  fonts[2] = fnt_CreateFont("gfx/font3_16_16.png", 
      "ABCDEFGHIJKLMNOPQRST"
      "UVWXYZ#>0123456789()"
      "              [_].,-", 16, 16);    
}

void
gynvael_MakeAScene()
{
  switch(g_GameMode)
  {
    case GAME_MODE_INTRO:
      {
        // Render intro
        glLoadIdentity();
         
        glTranslatef(0.0f, 0.0f, -5.0f);
        glScalef(1.0f, -1.0f, 1.0f);

        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, textures[TEX_VXLOGO]);
        draw_quad(-3.1, -2.5, 8, 8, 1,1,1);
        glDisable(GL_TEXTURE_2D); 
      }
      break;

    case GAME_MODE_BEFORE_LEVEL:
      // XXX
      // TO GYNVAEL: Tutaj implementujesz renderowanie pre-level image
      // na podstawie danych z g_GameState i g_GameState->currentLevel 
      // (ktore sa juz zaladowane)
      //
      {
        // Render intro
        // Pre-render initialization
        glLoadIdentity();
         
        glTranslatef(0.0f, 0.0f, -5.0f);
        glScalef(1.0f, -1.0f, 1.0f);

        glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_BG2]);
        draw_quad(-3.9, -2.9, 11.0, 11.0, 1,1,1);

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 512, 32, 
              "LEVEL %i", g_GameState.levelNumber));
        draw_quad(-3.5, -2.5, 7.0, 0.45, 1,1,1);

        float factx = 0.2f;

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 1024, 1024, 
              "        WITH THESE %i MAGES \n"
              "\n"
              "\n\n" // 1
              "\n\n" // 2
              "\n\n" // 3
              "\n\n" // 4
              "\n"
              "YOU MUST PASS %i MAPS IN %i SECONDS\n",
              g_GameState.currentLevel->playerCount,
              g_GameState.currentLevel->mapCount,
              (int)g_GameState.currentLevel->deadline              
              ));
        draw_quad(-3.0- factx, -1.0, 12, 12.0, 1.0,1.0,1.0);

        for( int i = 0; i < g_GameState.currentLevel->playerCount; i++ )
        {
          const char *mage_names[]  = { "FIRE MAGE", "TRIGGER MAGE", "TELEPORT MAGE", "SUPPORT MAGE" };
          const char *skill_names[] = { "FIREBALLS", "BULLETS", "TELEPORTS", "BRIDGES" };          

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 512, 64, 
              "%14s with %i %s",
              mage_names[g_GameState.players[i].params[0]],
              g_GameState.players[i].params[1],
              skill_names[g_GameState.players[i].params[0]]));

        draw_quad(-3.0- factx, -0.5 + i * 0.3f, 6, 0.75f, 1.0,1.0,1.0);

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 512, 64, 
              "%14s      %i %s",
              mage_names[g_GameState.players[i].params[0]],
              g_GameState.players[i].params[1],
              skill_names[g_GameState.players[i].params[0]]));

        draw_quad(-3.0- factx, -0.5 + i * 0.3f, 6, 0.75f, 1.0,1.0,0.0);

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 512, 64, 
              "%14s      %i",
              "",
              g_GameState.players[i].params[1]));

        draw_quad(-3.0 - factx, -0.5 + i * 0.3f, 6, 0.75f, 1.0,0.0,1.0);        
       }

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 1024, 1024, 
              "                   %i       \n"
              "\n"
              "\n\n" // 1
              "\n\n" // 2
              "\n\n" // 3
              "\n\n" // 4
              "\n"
              "              %i         %i        \n",
              g_GameState.currentLevel->playerCount,
              g_GameState.currentLevel->mapCount,
              (int)g_GameState.currentLevel->deadline              
              ));
        draw_quad(-3.0- factx, -1.0, 12, 12.0, 1.0,1.0,0.0);


        

        // Post-render operations
        glDisable(GL_BLEND);
        glDisable(GL_TEXTURE_2D);
      }

      break;

    case GAME_MODE_ABOUT:
      {
        if(g_AboutPage > 5 )
          g_AboutPage = 5;

        // Render intro
        // Pre-render initialization
        glLoadIdentity();
         
        glTranslatef(0.0f, 0.0f, -5.0f);
        glScalef(1.0f, -1.0f, 1.0f);

        glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_BG6]);
        draw_quad(-3.9, -2.9, 11.0, 11.0, 1,1,1);

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 512, 32, 
              "INFO (PAGE %i)", g_AboutPage));
        draw_quad(-3.5, -2.5, 7.0, 0.45, 1,1,1);

        switch(g_AboutPage)
        {
          case 0:
            {
        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 512, 1024, 
              " S T O R Y\n"
              "[_________]\n\n"
              " FOUR POWERFUL MAGES WERE\n"
              " RETURNING HOME FROM A TERRIBLE\n"
              " WAR WITH GREAT EVIL THAT\n"
              " HAUNTED THEIR REALMS."
              "\n"
              " AT THE SAME TIME SCIENTISTS\n"
              " AT CERN STARTED A NEW\n"
              " EXPERIMENT."
              "\n"
              " AND BLACK HOLE WAS CREATED...\n"
              "\n"
              " AND HIT BY LIGHTNING...\n"
              "\n"
              " THE LIGHTNING-BLACKHOLE TURNED\n"
              " INTO A MAGICAL INTER-REALM\n"
              " WORMHOLE AND TELEPORTED THE\n"
              " MAGES...\n"
              "\n"
              " STREIGHT INTO THE CERN MAIN\n"
              " COMPUTER.\n"
              "\n"
              " HELP THEM GET OUT AND RETURN\n"
              " TO THEIR REALM.\n"
              ));
        draw_quad(-0.7, -1.0, 4.5, 9.0, 1.0,1.0,1.0);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_TUT_STORY]);
        draw_quad(-4.0, -2.0, 4.0, 4.0, 1.0,1.0,1.0);        

            }
            break;

          case 1:
            {
        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 512, 1024, 
              " G A M E P L A Y\n"
              "[_______________]\n\n"
              " > USE 1 TO 4 MAGES IN TURNS\n"
              "\n"
              " > THE NEXT MAGE WILL SEE THE\n"
              "   OTHER MAGES MOVE AND DO\n"
              "   WHAT YOU DID IN PREVIOUS\n"
              "   TURNS\n"
              "\n"
              " > SYNCHRONIZE THEIR ACTIONS\n"
              "   TO PASS THE LEVELS\n"
              "\n"
              " > WALK THROUGH LEVEL EXIT\n"
              "\n"
              " > SOME LEVELS HAVE MORE THAN\n"
              "   ONE MAP\n"
              "\n"
              " GOOD LUCK#"
              ));
        draw_quad(-0.7, -1.0, 4.5, 9.0, 1.0,1.0,1.0);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_TUT_EXIT]);
        draw_quad(-4.0, -2.0, 4.0, 4.0, 1.0,1.0,1.0);        

            }
            break;                                                

          case 2:
            {
        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 512, 1024, 
              " F I R E   M A G E\n"
              "[_________________]\n\n"
              " THE FIRE MAGE CAN CAST\n"
              " POWERFUL FIREBALLS THAT BURN\n"
              " THROUGH THE SILICON OF THE CPU\n"
              " \n"
              " USE THE FIRE MAGE TO TURN\n"
              "   WALLS\n"
              " INTO\n"
              "   HOLES\n"
              "\n"
              "\n"
              " >HINT\n"
              "  SUPPORT MAGE CAN FILL HOLES\n"
              "  WITH BRIDGES\n"
              ));
        draw_quad(-0.7, -1.0, 4.5, 9.0, 1.0,1.0,1.0);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_TUT_FIRE]);
        draw_quad(-4.2, -2.0, 4.0, 4.0, 1.0,1.0,1.0);        

            }
            break;            

          case 3:
            {
        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 512, 1024, 
              " T R I G G E R   M A G E\n"
              "[_______________________]\n\n"
              " THE TRIGGER MAGE SUMMONS AND\n"
              " SHOOTS A HUGE METALIC OBJECT\n"
              " THAT CAN FLIP THE REGISTER\n"
              " VALUES\n"
              "\n"
              " USE THE TRIGGER MAGE TO\n"
              " TRIGGER THE SWITCHES\n"
              "\n"
              "\n"
              " >HINT\n"
              "  SOMETIMES SHOOTING BLINDLY\n"
              "  AND DESTROYING WALLS LATER\n"
              "  DOES THE JOB"
              ));
        draw_quad(-0.7, -1.0, 4.5, 9.0, 1.0,1.0,1.0);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_TUT_TRIGGER]);
        draw_quad(-4.0, -2.0, 4.0, 4.0, 1.0,1.0,1.0);        

            }
            break;                        


          case 4:
            {
        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 512, 1024, 
              " T E L E P O R T   M A G E\n"
              "[_________________________]\n\n"
              " THIS POWERFUL WIZARD CAN\n"
              " CREATE A SHORT DISTANCE PORTAL\n"
              " THROUGH SPACE FOR HIMSELF AND\n"
              " OTHERS\n"
              "\n"
              " USE THE TELEPORT MAGE TO\n"
              " CREATE IN AND OUT PORTALS\n"
              "\n"
              "\n"
              " >HINT\n"
              "  YOU CAN USE TELEPORTS ACROSS\n"
              "  MAPS"
              ));
        draw_quad(-0.7, -1.0, 4.5, 9.0, 1.0,1.0,1.0);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_TUT_TELE]);
        draw_quad(-4.0, -2.0, 4.0, 4.0, 1.0,1.0,1.0);        

            }
            break;   

          case 5:
            {
        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 512, 1024, 
              " S U P P O R T   M A G E\n"
              "[_______________________]\n\n"
              " THE SUPPORT MAGE CAN CONJURE\n"
              " MAGICAL BRIDGES TO FILL THE\n"
              " GAPS IN THE CPU FLOOR\n"
              "\n"
              " USE THE SUPPORT MAGE TO\n"
              " MAKE HOLES WALKABLE\n"
              "\n"
              "\n"
              " >HINT\n"
              "  DO NOT BE SELFISH, CREATE\n"
              "  BRIDGES FOR OTHER MAGES TOO"
              ));
        draw_quad(-0.7, -1.0, 4.5, 9.0, 1.0,1.0,1.0);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_TUT_BRIDGE]);
        draw_quad(-4.0, -2.0, 4.0, 4.0, 1.0,1.0,1.0);        

            }
            break;                                    
        }




        // Post-render operations
        glDisable(GL_BLEND);
        glDisable(GL_TEXTURE_2D);
      }
      break;

    case GAME_MODE_LVL_FINISHED:
      {
        // Render intro
        // Pre-render initialization
        glLoadIdentity();
         
        glTranslatef(0.0f, 0.0f, -5.0f);
        glScalef(1.0f, -1.0f, 1.0f);

        glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_BG2]);
        draw_quad(-3.9, -2.9, 11.0, 11.0, 1,1,1);

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 512, 32, 
              "LEVEL %i", g_GameState.levelNumber - 1));
        draw_quad(-3.5, -2.5, 7.0, 0.45, 1,1,1);


        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 512, 32, 
              "COMPLETED"));
        draw_quad(-0.5, 2.2, 7.0, 0.45, 1,1,1);
        

        // Post-render operations
        glDisable(GL_BLEND);
        glDisable(GL_TEXTURE_2D);
      }
      break;

    case GAME_MODE_GAME_FINISHED:
      {
        // Render intro
        // Pre-render initialization
        glLoadIdentity();
         
        glTranslatef(0.0f, 0.0f, -5.0f);
        glScalef(1.0f, -1.0f, 1.0f);

        glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_BG2]);
        draw_quad(-3.9, -2.9, 11.0, 11.0, 1,1,1);

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 512, 32, 
              "GAME"));
        draw_quad(-3.5, -2.5, 7.0, 0.45, 1,1,1);


        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 512, 32, 
              "COMPLETED"));
        draw_quad(-0.5, 2.2, 7.0, 0.45, 1,1,1);
        

        // Post-render operations
        glDisable(GL_BLEND);
        glDisable(GL_TEXTURE_2D);
      }
      break;

    case GAME_MODE_MENU:
      // Render the menu
      {
        float x[] = {-3.6f, -3.6f, -3.6f};
        float y[] = {-0.5f, 0.0f, 0.5f};

        // Pre-render initialization
        glLoadIdentity();
         
        glTranslatef(0.0f, 0.0f, -5.0f);
        glScalef(1.0f, -1.0f, 1.0f);

        glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_BG3]);
        draw_quad(-3.9, -2.9, 11.0, 11.0, 1,1,1);

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 512, 32, 
              "MAGUS EX MACHINA"));
        draw_quad(-3.5, -2.5, 7.0, 0.45, 1,1,1);        

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[1], 1024, 64, 
              "           48h GAMEJAM-ZRH 2011 VEXILLIUM#"));
        draw_quad(-4.42, -2.0, 8.0, 0.60, 1,1,1);

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[2], 1024, 1024, 
              " C R E D I T S\n"
              "[_____________]\n\n"
              " # design, code, etc\n"
              " mateusz j00ru jurczyk\n"
              " gynvael coldwind\n"
              "\n"
              " # gfx on creative commons by\n"
              " Kevin Saunders\n"
              " Peter Shanks\n"
              "\n"
              " # music by\n"
              " tomasz wacirz\n"
              " ((c) gynvael coldwind)\n"
              "\n"
              " # sfx by\n"
              " SoundJay.com\n"
              "\n"
              " # libs used\n"
              " SDL, SDL_image, SDL_mixer\n"
              " Powered by OpenGL\n"
              ));
        draw_quad(-0.5, -1.0, 9, 9.0, 1.0,1.0,1.0);


        // Render the "play" item
        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 256, 32, 
              "PLAY"));
        draw_quad(-3.3, -0.5, 2.0, 0.25, 1,1,1);

        // Render the "about" item
        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 256, 32, 
              "INFO"));
        draw_quad(-3.3, 0, 2.0, 0.25, 1,1,1);

        // Render the "exit" item
        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 256, 32, 
              "EXIT"));
        draw_quad(-3.3, 0.5, 2.0, 0.25, 1,1,1);

        // Render the arrow
       static float h_fx;
       h_fx += ratio * 3.0f;
       
       glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 32, 32, ">"));
        draw_quad(x[g_MenuItem % (sizeof(x)/sizeof(x[0]))] - fabs(sinf(h_fx)) * 0.10,
                  y[g_MenuItem % (sizeof(y)/sizeof(y[0]))], 0.25, 0.25, 1,1,1);

        // Post-render operations
        glDisable(GL_BLEND);
        glDisable(GL_TEXTURE_2D);
      }
      break;

    case GAME_MODE_LOSE:
      {
        // Render intro
        // Pre-render initialization
        glLoadIdentity();
         
        glTranslatef(0.0f, 0.0f, -5.0f);
        glScalef(1.0f, -1.0f, 1.0f);

        glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);

        glBindTexture(GL_TEXTURE_2D, textures[TEX_BG2]);
        draw_quad(-3.9, -2.9, 11.0, 11.0, 1,1,1);

        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 512, 32, 
              "LEVEL %i", g_GameState.levelNumber));
        draw_quad(-3.5, -2.5, 7.0, 0.45, 1,1,1);


        glBindTexture(GL_TEXTURE_2D, 
            fnt_TextToTexture(fonts[0], 512, 32, 
              "FAILED"));
        draw_quad(0.8, 2.2, 7.0, 0.45, 1,0,0);
        

        // Post-render operations
        glDisable(GL_BLEND);
        glDisable(GL_TEXTURE_2D);
      }
      break;    

    case GAME_MODE_GAMEPLAY:
      {
        // Render bg
        glLoadIdentity();
         
        glTranslatef(0.0f, 0.0f, -5.0f);
        glScalef(1.0f, -1.0f, 1.0f);

        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, textures[TEX_BACKGROUND]);
        draw_quad(-4, -3, 8, 6, 1,1,1);
        glDisable(GL_TEXTURE_2D); 

        glColor3f(1,1,1);


        // Game shown from here:  
        glLoadIdentity();
        glClear(GL_DEPTH_BUFFER_BIT);  

        glTranslatef(0.0f, 0.0f, -25.0f);

        glScalef(1.0f, -1.0f, 1.0f);

        int map_id = g_GameState.currentLevel->activeMap; 

        glTranslatef(-((float)g_GameState.currentLevel->maps[map_id]->width / 2.0f),
                     -((float)g_GameState.currentLevel->maps[map_id]->height / 2.0f), 0.0f);

        gynvael_DrawMap(map_id); // id from current level
        // Players are rendered in map.
        // So are objects.

        gynvael_DrawHUD();
      }
    break;
  }

}





void gynvael_RenderPlayer(uint8_t id)
{
  PLAYER_STRUCT *p = &g_GameState.players[id];
  /*float p_w = 0.5f;
  float p_h = 0.5f;

  float p_x = p->x - p_w/2.0f;
  float p_y = p->y - p_h/2.0f;*/

  // bail out if the given player is not present on the currently rendered map
  if(p->map != g_GameState.currentLevel->activeMap)
    return;

  //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  
  /*glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glBindTexture(GL_TEXTURE_2D, textures[TEX_PLAYER_TEMP]);
  draw_quad(p_x, p_y, p_w, p_h, 1.0f, 1.0f, 1.0f);
  glDisable(GL_BLEND);
  glDisable(GL_TEXTURE_2D); */

  float player_colors[4][3] = {
    { 1.0, 0.6, 0.6 }, // Fireball-guy
    { 0.6, 0.6, 1.0 }, // TriggerBullet-guy
    { 1.0, 0.6, 1.0 }, // Teleport-guy
    { 0.6, 1.0, 0.6 }  // Bridge-guy
  };

  // Did player move?
  bool moved = (p->pos & 0x80000000);
  p->pos = (int)(((unsigned int)p->pos) & 0x7fffffff);

  float rot = float(p->pos) * 45.0f;

  glColor3fv(player_colors[p->params[0]]); // params 0 is type
  float w = 1.5f;
  float h = 1.5f;
  
  rot += 45.0f * ratio;
  glPushMatrix();
  glTranslatef(p->x, p->y, 0.0f);

  glRotatef(rot, 0, 0, 1);
  glTranslatef(- w/2.0f, - h/2.0f, 0.0f);

  if(moved)
    gynvael_DrawAnim(0,0, w, h, SPRITE_PLAYER, 0, 0, 0.1f);
  else
    gynvael_DrawAnim(0,0, w, h, SPRITE_PLAYER, 2, 3, 0.1f);

  glPopMatrix();
  glColor3f(1,1,1);
}

void
gynvael_DrawMap(int id)
{
  MAP_STRUCT *map = g_GameState.currentLevel->maps[id];
  int map_width  = map->width;
  int map_height = map->height;

  //float fmap_width  = (float)map_width;
  //float fmap_height = (float)map_height;

  //glLineWidth(2);

  // Start with translate.

  glPushMatrix();


  // TopLeft == 0.0f x 0.0f
  float dx, dy;

  int x, y;

  // Draw phases:
  // 1. Underground: Hole [Hole with bridge]
  // 2. Floor level: Floor
  // 3. On floor   : Players, Iteams, etc
  // 4. Walls      : Wall

  // ----------------------------------------------------------------
  // Underground
  // ----------------------------------------------------------------
  for(y = 0, dy = 0.0f; y < map_height; y++, dy += 1.0f)
  {
    TILE_STRUCT *row = map->tiles[y];

    for(x = 0, dx = 0.0f; x < map_width; x++, dx += 1.0f)
    {
      TILE_STRUCT *tile = &row[x];
      if(tile->tileType != TILE_HOLE)
        continue;

      uint32_t surround = 0; // bits: top bottom left right (set if non-wall)
      surround |= (bool)(y == 0                || map->tiles[y - 1][x].tileType != TILE_HOLE) << 3;
      surround |= (bool)(y == (map_height - 1) || map->tiles[y + 1][x].tileType != TILE_HOLE) << 2;
      surround |= (bool)(x == 0                || row[(x - 1)].tileType != TILE_HOLE) << 1;
      surround |= (bool)(x == (map_width - 1)  || row[(x + 1)].tileType != TILE_HOLE) << 0;
      gynvael_DrawHole(dx, dy, surround);
    }
  }
  
  // ----------------------------------------------------------------
  // Floor level.
  // ----------------------------------------------------------------
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, textures[TEX_TILE_FLOOR]);
  glEnable(GL_BLEND);
  glColor4f(0.0f, 0.1f, 0.5f, 0.95f);

  for(y = 0, dy = 0.0f; y < map_height; y++, dy += 1.0f)
  {
    TILE_STRUCT *row = map->tiles[y];

    for(x = 0, dx = 0.0f; x < map_width; x++, dx += 1.0f)
    {
      TILE_STRUCT *tile = &row[x];

      switch(tile->tileType)
      {
        case TILE_HOLE:    // rendered in previous phase
          if(tile->params[2] == 1)
          {
            draw_quad_coords(dx, dy, 5.0f, 5.0f, 1.0f, 1.0f);          
            glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
            glBindTexture(GL_TEXTURE_2D, textures[TEX_TILE_BRIDGE]);
            draw_quad(dx, dy, 1, 1, 1,1,1);
            glBindTexture(GL_TEXTURE_2D, textures[TEX_TILE_FLOOR]);          
            glColor4f(0.0f, 0.1f, 0.5f, 0.95f);          
          }
          break;


        case TILE_WALL:    // will be rendered later
        case TILE_NOTHING: // never rendered
          continue;

        case TILE_STD_BUTTON: // floor level
          draw_quad_coords(dx, dy, 5.0f, 5.0f, 1.0f, 1.0f);          
          glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
          gynvael_DrawTile_StdButton(dx, dy, tile);
          glEnable(GL_TEXTURE_2D);       
          glEnable(GL_BLEND);
          glBindTexture(GL_TEXTURE_2D, textures[TEX_TILE_FLOOR]);          
          glColor4f(0.0f, 0.1f, 0.5f, 0.95f);          
          break;          

        default: // Floor
          draw_quad_coords(dx, dy, 5.0f, 5.0f, 1.0f, 1.0f);
          break;
      }
    }
  }
  glColor4f(1,1,1,1);  
  glDisable(GL_TEXTURE_2D);            
  glDisable(GL_BLEND);  

  // ----------------------------------------------------------------
  // Now draw the players and objects.
  // ----------------------------------------------------------------
  player_RenderAll();

  for(y = 0, dy = 0.0f; y < map_height; y++, dy += 1.0f)
  {
    TILE_STRUCT *row = map->tiles[y];

    for(x = 0, dx = 0.0f; x < map_width; x++, dx += 1.0f)
    {
      TILE_STRUCT *tile = &row[x];

      // Simple switch case.
      switch(tile->tileType)
      {
        case TILE_END_POINT    : gynvael_DrawTile_EndPoint(dx, dy, tile); break;
        case TILE_EXIT         : gynvael_DrawTile_ExitPoint(dx, dy, tile); break;
        case TILE_TOGGLE_BUTTON: gynvael_DrawTile_ToggleButton(dx, dy, tile); break;
        case TILE_TELEPORT     : gynvael_DrawTile_Teleport(dx, dy, tile); break;

        default:
          continue;
      }
    }
  }

  // Object.
  size_t obj_size = g_Objects.size();
  size_t n = 0;
  for(n = 0; n < obj_size; n++)
  {
    OBJECT_STRUCT *obj = g_Objects[n];

    // Render only objects visible on this map;
    if(obj->map_id != id)
      continue;
    
    switch(obj->type)
    {
      case OBJECT_TYPE_SPELL_BULLET:
        gynvael_DrawAnimCenter(obj->x,obj->y,obj->w,obj->h,SPRITE_SPELL_BULLET, 0,0, 0.07f);
        break;

      case OBJECT_TYPE_SPELL_FIREBALL:
        gynvael_DrawAnimCenter(obj->x,obj->y,obj->w,obj->h,SPRITE_SPELL_FIREBALL, 0,0, 0.07f);
        break;

      default:
        continue;
    }
  } 

  
  // ----------------------------------------------------------------
  // Wall level
  // ----------------------------------------------------------------
  for(y = 0, dy = 0.0f; y < map_height; y++, dy += 1.0f)
  {    
    TILE_STRUCT *row = map->tiles[y];
    for(x = 0, dx = 0.0f; x < map_width; x++, dx += 1.0f)
    {
      TILE_STRUCT *tile = &row[x];

      if(tile->tileType != TILE_WALL)
      {
        glColor3f(1,1,1);
        switch(tile->tileType)
        {        
          case TILE_DOOR_V       : gynvael_DrawTile_Door(dx, dy, tile, true); break;
          case TILE_DOOR_H       : gynvael_DrawTile_Door(dx, dy, tile, false); break; 
          default:
            continue;
        }
        continue;        
      }


      // TODO: opt this
      uint32_t surround = 0; // bits: top bottom left right (set if non-wall)
      surround |= (bool)(y == 0                || map->tiles[y - 1][x].tileType != TILE_WALL) << 3;
      surround |= (bool)(y == (map_height - 1) || map->tiles[y + 1][x].tileType != TILE_WALL) << 2;
      surround |= (bool)(x == 0                || row[(x - 1)].tileType != TILE_WALL) << 1;
      surround |= (bool)(x == (map_width - 1)  || row[(x + 1)].tileType != TILE_WALL) << 0;

      gynvael_DrawWall(dx, dy, surround);
    }
  }

  // GUI level.
  // Draw active player marker.
  PLAYER_STRUCT *ap = &g_GameState.players[g_GameState.activePlayer];
  static float bounce;
  bounce += ratio * 4.0f;
 
  float ptr_wh = 2.5f + sinf(bounce) * 0.5f;
  glBindTexture(GL_TEXTURE_2D, textures[TEX_PLAYER_PTR]);
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);

  draw_quad(ap->x -(ptr_wh/2.0f), ap->y -(ptr_wh/2.0f), ptr_wh, ptr_wh, 1,1,1);

  // Draw the mouse pointer
  glBindTexture(GL_TEXTURE_2D, textures[TEX_MOUSE_PTR]);

  draw_quad(mmx,mmy, 2,2, 1,1,1);

  // Draw targeter only if player type is FIRE or BULLET
  if(ap->params[0] == 0 || ap->params[0] == 1)
  {
    glBindTexture(GL_TEXTURE_2D, textures[TEX_TARGET_PTR]);
    draw_quad(ap->x + target_vx * 2.0f - 0.25f, ap->y + target_vy * 2.0f - 0.25f, 0.5f, 0.5f, 1,1,1);  
  }

  // Draw selector only if player type is TELEPORT or BRIDGE
  if(ap->params[0] == 2 || ap->params[0] == 3)
  {
    // Draw red for bridge out of range
    if(ap->params[0] == 3 && (
        ((target_floor_x - ap->x) * (target_floor_x - ap->x) +
         (target_floor_y - ap->y) * (target_floor_y - ap->y) > 3.0f * 3.0f)
        ||
        (target_floor_x >= 0 && target_floor_y >= 0 && target_floor_x < map->width && target_floor_y < map->height &&
         (map->tiles[target_floor_y][target_floor_x].tileType != TILE_HOLE ||
          map->tiles[target_floor_y][target_floor_x].params[2] != 0))
        ))
    {
      gynvael_DrawAnim(target_floor_x, target_floor_y, 1, 1, SPRITE_MIX1, 5, 6, 1.0f);      

    }
    else
    {
      gynvael_DrawAnim(target_floor_x, target_floor_y, 1, 1, SPRITE_MIX1, 4, 5, 1.0f);
    }
  }



  glDisable(GL_BLEND);    
  glDisable(GL_TEXTURE_2D);  
  //printf("%f, %f\n",a ,b);


  glPopMatrix();  
}

void gynvael_DrawHUD()
{
  float x = -3.5f, y = 1.7f;
  float w =  1.5f, h = 0.3f;

  glLoadIdentity();

  glTranslatef(0.0f, 0.0f, -5.0f);
  glScalef(1.0f, -1.0f, 1.0f);

  // Time left
  y = 2.9-0.25;
  x = -2.8;
  h = 0.35;
  w = 3.0 + 3.9;

  glColor3f(0,0,0);

  glBegin(GL_LINES);
  glVertex3f(x,    y, 00.0f);
  glVertex3f(x+w,  y, 00.0f);
  glEnd();

  glBegin(GL_LINES);
  glVertex3f(x,    y+h, 00.0f);
  glVertex3f(x+w,  y+h, 00.0f);
  glEnd();

  glBegin(GL_LINES);
  glVertex3f(x,  y, 00.0f);
  glVertex3f(x,  y+h, 00.0f);
  glEnd();

  glBegin(GL_LINES);
  glVertex3f(x+w,  y, 00.0f);
  glVertex3f(x+w,  y+h, 00.0f);
  glEnd();

  glColor3f(1,1,1);  

  glBegin(GL_QUADS);
  glTexCoord2f(0.0f, 0.0f); glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(x + 0.01, y + 0.001);

  glTexCoord2f(1.0f, 0.0f); glColor3f(0.0f, 1.0f, 0.0f);
  glVertex2f(x + (w * (g_GameState.timeLeft / g_GameState.currentLevel->deadline)) + 0.01, y + 0.001);

  glTexCoord2f(1.0f, 1.0f); glColor3f(0.0f, 1.0f, 0.0f);
  glVertex2f(x + (w * (g_GameState.timeLeft / g_GameState.currentLevel->deadline)) + 0.01, y + h - 0.01);

  glTexCoord2f(0.0f, 1.0f); glColor3f(1.0f, 0.0f, 0.0f); glVertex2f(x + 0.01, y + h - 0.01);
  glEnd();  

  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);

  glBindTexture(GL_TEXTURE_2D, 
    fnt_TextToTexture(fonts[0], 1024, 32, 
    "LEVEL %i (MAGE %i of %i)", g_GameState.levelNumber,
    g_GameState.activePlayer,
    g_GameState.currentLevel->playerCount
    ));
    draw_quad(-3.9, -2.9, 12.0, 0.35, 1,1,1);

  glBindTexture(GL_TEXTURE_2D, 
    fnt_TextToTexture(fonts[0], 1024, 32, 
    "%2i", int(g_GameState.timeLeft)
    ));

  glPushMatrix();
  if(g_GameState.timeLeft <= 5.0f)
  {
    int tl = int(g_GameState.timeLeft);
    float tlf = g_GameState.timeLeft;
    tlf -= tl;

    tlf /= 5.0f;
    draw_quad(-3.9-tlf/2.0f, 2.8-0.35-tlf/2.0f, 12.0+tlf, 0.35+tlf, 1,1,1);    
  }
  else
  {
    draw_quad(-3.9, 2.8-0.35, 12.0, 0.35, 1,1,1);    
  }




  glPopMatrix();

  glDisable(GL_TEXTURE_2D);
  glDisable(GL_BLEND);        

}


#define DRAWWALL_WORKER \
{\
  if(top) { glVertex3f(x0, y0, h); glVertex3f(x1, y0, h); glVertex3f(x1, y0, 0); glVertex3f(x0, y0, 0); } \
  if(bottom) { glVertex3f(x0, y1, h); glVertex3f(x1, y1, h); glVertex3f(x1, y1, 0); glVertex3f(x0, y1, 0); } \
  if(left) { glVertex3f(x0, y0, h); glVertex3f(x0, y1, h); glVertex3f(x0, y1, 0); glVertex3f(x0, y0, 0);  } \
  if(right) { glVertex3f(x1, y0, h); glVertex3f(x1, y1, h); glVertex3f(x1, y1, 0); glVertex3f(x1, y0, 0);  } \
}

void gynvael_DrawWall(float dx, float dy, uint32_t surround)
{
  // Assume: Blend disabled, Textures disabled
  bool top    = !!(surround & 8); // TODO
  bool bottom = !!(surround & 4);
  bool left   = !!(surround & 2);
  bool right  = !!(surround & 1);

  float x0 = dx;        // left
  float x1 = dx + 1.0f; // right
  float y0 = dy;        // top
  float y1 = dy + 1.0f; // bottom

  float h = 1.0f;



  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  glColor4f(0.0f, 0.2f, 1.0f, 1.0f);
  glBegin(GL_QUADS);  
  //glVertex3f(x0, y0, h);  glVertex3f(x1, y0, h);  glVertex3f(x1, y1, h);  glVertex3f(x0, y1, h);  
  DRAWWALL_WORKER;
  glEnd();
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

  glEnable(GL_BLEND);
  glColor4f(0.0f, 0.1f, 0.7f, 0.8f);
  glBegin(GL_QUADS);  
  glVertex3f(x0, y0, h);  glVertex3f(x1, y0, h);  glVertex3f(x1, y1, h);  glVertex3f(x0, y1, h);
  DRAWWALL_WORKER;
  glEnd();

  glDisable(GL_BLEND);



}

#define CA glColor4f(0.0f, 0.1f, 0.7f, 0.6f)
#define CB glColor4f(0.0f, 0.0f, 0.0f, 1.0f)

#define DRAWHOLE_WORKER \
{\
  if(top) { CB; glVertex3f(x0, y0, h); glVertex3f(x1, y0, h); CA; glVertex3f(x1, y0, 0); glVertex3f(x0, y0, 0); } \
  if(bottom) { CB; glVertex3f(x0, y1, h); glVertex3f(x1, y1, h); CA; glVertex3f(x1, y1, 0); glVertex3f(x0, y1, 0); } \
  if(left) { CB; glVertex3f(x0, y0, h); glVertex3f(x0, y1, h); CA; glVertex3f(x0, y1, 0); glVertex3f(x0, y0, 0);  } \
  if(right) { CB; glVertex3f(x1, y0, h); glVertex3f(x1, y1, h); CA; glVertex3f(x1, y1, 0); glVertex3f(x1, y0, 0);  } \
}

void gynvael_DrawHole(float dx, float dy, uint32_t surround)
{
  // Assume: Blend disabled, Textures disabled
  bool top    = !!(surround & 8); // TODO
  bool bottom = !!(surround & 4);
  bool left   = !!(surround & 2);
  bool right  = !!(surround & 1);

  float x0 = dx;        // left
  float x1 = dx + 1.0f; // right
  float y0 = dy;        // top
  float y1 = dy + 1.0f; // bottom

  float h = -2.5f;



  /*
  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  glColor4f(0.0f, 0.2f, 1.0f, 1.0f);
  glBegin(GL_QUADS);  
  glVertex3f(x0, y0, h);  glVertex3f(x1, y0, h);  glVertex3f(x1, y1, h);  glVertex3f(x0, y1, h);  
  DRAWHOLE_WORKER;
  glEnd();
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);*/

  glEnable(GL_DEPTH_TEST);

  glBegin(GL_QUADS);  

  CB;
  glVertex3f(x0, y0, h);  glVertex3f(x1, y0, h);  glVertex3f(x1, y1, h);  glVertex3f(x0, y1, h);
  DRAWHOLE_WORKER;
  glEnd();

  glDisable(GL_DEPTH_TEST);
}

#undef CA
#undef CB


void gynvael_DrawTile_Door(float dx, float dy, TILE_STRUCT *tile, bool vert)
{
#define DRAWDOOR_V_WORKER \
  glVertex3f(xc, y0, h); glVertex3f(xc, y1, h); glVertex3f(x0, y1, 0); glVertex3f(x0, y0, 0); \
  glVertex3f(xc, y0, h); glVertex3f(xc, y1, h); glVertex3f(x1, y1, 0); glVertex3f(x1, y0, 0); 

#define DRAWDOOR_H_WORKER \
  glVertex3f(x0, yc, h); glVertex3f(x1, yc, h); glVertex3f(x1, y0, 0); glVertex3f(x0, y0, 0); \
  glVertex3f(x0, yc, h); glVertex3f(x1, yc, h); glVertex3f(x1, y1, 0); glVertex3f(x0, y1, 0);

  // Assume: Blend disabled, Textures disabled
  float x0 = dx + (vert ? 0.1f : 0.0f); // left
  float x1 = dx + (vert ? 0.9f : 1.0f); // right
  float xc = dx + 0.5f; // center

  float y0 = dy + (!vert ? 0.1f : 0.0f); // top
  float y1 = dy + (!vert ? 0.9f : 1.0f); // bottom
  float yc = dy + 0.5f; // center

  float h = 0.8f;

  float off_color1[] = { 1.0f, 0.2f, 0.2f, 1.0f };
  float off_color2[] = { 0.6f, 0.0f, 0.0f, 0.5f };  

  float on_color1[] = { 0.2f, 1.0f, 0.2f, 1.0f };
  float on_color2[] = { 0.0f, 0.7f, 0.0f, 0.5f }; 

  bool is_open = g_ObjectParams[tile->params[0]][0];

  if(is_open)
    h = 0.05f;

  // Lines
  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  glColor4fv(is_open ? on_color1 : off_color1);
  glBegin(GL_QUADS);
  if(vert) { DRAWDOOR_V_WORKER; } else { DRAWDOOR_H_WORKER; }
  glEnd();
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

  // Body
  glEnable(GL_BLEND);
  glColor4fv(is_open ? on_color2 : off_color2);
  glBegin(GL_QUADS);  
  if(vert) { DRAWDOOR_V_WORKER; } else { DRAWDOOR_H_WORKER; }
  glEnd();

  glDisable(GL_BLEND);  

#undef DRAWDOOR_WORKER

  
}


void gynvael_DrawTile_EndPoint(float dx, float dy, TILE_STRUCT *tile) 
{
  (void)tile;
  gynvael_DrawAnim(dx, dy, 1,1, SPRITE_END_LEVEL, 9, 0, 0.07f);
}

void gynvael_DrawTile_ExitPoint(float dx, float dy, TILE_STRUCT *tile) 
{
  (void)tile;
  gynvael_DrawAnim(dx, dy, 1,1, SPRITE_EXIT_LEVEL, 9, 0, 0.07f);
}

void gynvael_DrawTile_Teleport(float dx, float dy, TILE_STRUCT *tile)
{
  if(g_ObjectParams[tile->params[0]][0] != -1)
    gynvael_DrawAnim(dx, dy, 1,1, SPRITE_TELEPORT_IN, 9, 0, 0.05f);  
  else
    gynvael_DrawAnim(dx, dy, 1,1, SPRITE_TELEPORT_OUT, 9, 0, 0.05f);
}

void gynvael_DrawTile_StdButton(float dx, float dy, TILE_STRUCT *tile)
{
  if(!g_ObjectParams[tile->params[0]][0])
    gynvael_DrawAnim(dx, dy, 1,1, SPRITE_MIX1, 1, 2, 1.0f);  
  else
    gynvael_DrawAnim(dx, dy, 1,1, SPRITE_MIX1, 0, 1, 1.0f);
}


void gynvael_DrawTile_ToggleButton(float dx, float dy, TILE_STRUCT *tile)
{
  // TODO
  if(g_ObjectParams[tile->params[0]][0])
    gynvael_DrawAnim(dx, dy, 1,1, SPRITE_TOGGLE_BUTTON_ON, 9, 0, 0.05f);  
  else
    gynvael_DrawAnim(dx, dy, 1,1, SPRITE_TOGGLE_BUTTON_OFF, 9, 0, 0.08f);
  
}


void
draw_quad(float x, float y, float w, float h, float r, float g, float b)
{
  glBegin(GL_QUADS);
  glTexCoord2f(0.0f, 0.0f); glColor3f(r, g, b); glVertex2f(x + 0, y + 0);
  glTexCoord2f(1.0f, 0.0f); glColor3f(r, g, b); glVertex2f(x + w, y + 0);
  glTexCoord2f(1.0f, 1.0f); glColor3f(r, g, b); glVertex2f(x + w, y + h);
  glTexCoord2f(0.0f, 1.0f); glColor3f(r, g, b); glVertex2f(x + 0, y + h);
  glEnd();
}

void gynvael_DrawAnim(float dx, float dy, float w, float h, int anim_id, int start, int last, float delay)
{
  if(last == 0)
    last = g_sprites[anim_id].sprite_count;

  // Calc frame
  float tm = float(last_time) / 1000.0f;
  uint32_t num = (uint32_t)(tm / delay);
  num = num % (last - start);
  num += start;

  float u = g_sprites[anim_id].u_max;
  float v = g_sprites[anim_id].v_max;

  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  //glColor4f(1,1,1,1);
  glBindTexture(GL_TEXTURE_2D, g_sprites[anim_id].textures[num]);

  glBegin(GL_QUADS);
  glTexCoord2f(0.0f, 0.0f); glVertex2f(dx + 0, dy + 0);
  glTexCoord2f(u   , 0.0f); glVertex2f(dx + w, dy + 0);
  glTexCoord2f(u   , v   ); glVertex2f(dx + w, dy + h);
  glTexCoord2f(0.0f, v   ); glVertex2f(dx + 0, dy + h);
  glEnd();
  glDisable(GL_BLEND);
  glDisable(GL_TEXTURE_2D);
}



void
draw_quad_coords(float x, float y, float mw, float mh, float w, float h)
{
  float qx0 = x / mw;
  float qx1 = (x+1) / mw;
  float qy0 = y / mh;
  float qy1 = (y+1) / mh;

  glBegin(GL_QUADS);
  glTexCoord2f(qx0, qy0);glVertex2f(x + 0, y + 0);
  glTexCoord2f(qx1, qy0);glVertex2f(x + w, y + 0);
  glTexCoord2f(qx1, qy1);glVertex2f(x + w, y + h);
  glTexCoord2f(qx0, qy1);glVertex2f(x + 0, y + h);
  glEnd();
}


void
draw_quada(float x, float y, float w, float h, float r, float g, float b,float a)
{

  glBegin(GL_QUADS);
  glTexCoord2f(0.0f, 0.0f); glColor4f(r, g, b,a); glVertex2f(x + 0, y + 0);
  glTexCoord2f(1.0f, 0.0f); glColor4f(r, g, b,a); glVertex2f(x + w, y + 0);
  glTexCoord2f(1.0f, 1.0f); glColor4f(r, g, b,a); glVertex2f(x + w, y + h);
  glTexCoord2f(0.0f, 1.0f); glColor4f(r, g, b,a); glVertex2f(x + 0, y + h);
  glEnd();

}

void
draw_line_quad(float x, float y, float w, float h, float r, float g, float b)
{
  //printf("%f %f, %f %f,  %f %f %f\n", x,y,w,h,r,g,b);

  glBegin(GL_LINE_STRIP);
  glColor3f(r, g, b); glVertex2f(x + 0, y + 0);
  glColor3f(r, g, b); glVertex2f(x + w, y + 0);
  glColor3f(r, g, b); glVertex2f(x + w, y + h);
  glColor3f(r, g, b); glVertex2f(x + 0, y + h);
  glColor3f(r, g, b); glVertex2f(x + 0, y + 0);
  glEnd();
}

uint32_t ceil_pow2(uint32_t v)
{
  // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
  // compute the next highest power of 2 of 32-bit v
  v--;
  v |= v >> 1;
  v |= v >> 2;
  v |= v >> 4;
  v |= v >> 8;
  v |= v >> 16;
  v++;
  return v;
}

void make_sprite(int out_sprite_id, const char *file_name, int single_w, int single_h)
{
  // Load image.
  SDL_Surface *img;

  img = IMG_Load(file_name);
  if(!img)
    fprintf(stderr, "make_sprite: File %s not found", file_name);

  //{ FILE *f; f = fopen("a.raw", "wb"); fwrite(img->pixels, 1, img->w * img->h * 1, f); fclose(f); }


  // Convert to 32 bits.
  SDL_PixelFormat fmt = {
    NULL, 32, 4, 
    0, 0, 0, 0,
    0, 8, 16, 24,
    0xff, 0xff00, 0xff0000, 0xff000000,
    0,
    0xff
  };
  SDL_Surface *nimg = SDL_ConvertSurface(img, &fmt, SDL_SWSURFACE);

  // Transparentize.
  int w = nimg->w, h = nimg->h;

  int px_count = 0, tr_count = 0;

  uint32_t *p = (uint32_t*)nimg->pixels;
  for(int j = 0; j < h; j++)
  {
    for(int i = 0; i < w; i++, p++, px_count++)
    {
      uint32_t v = *p;
      if((v & 0xffffff) == 0)
      {
        v = 0; // Alpha + color to 0
        tr_count++;
      }
      else
      {
        v |= 0xff000000;
      }

      *p = v;
    }
  }

  // Setup the sprite.
  int count_columns = w / single_w;
  int count_rows    = h / single_h;
  int total = count_columns * count_rows;

  SPRITE_STURCT *out = &g_sprites[out_sprite_id];
  out->sprite_count = total;
  out->textures     = new unsigned int[total];

  uint32_t p2_single_w = ceil_pow2(single_w);
  uint32_t p2_single_h = ceil_pow2(single_h);

  out->u_max        = float(single_w) / float(p2_single_w);
  out->v_max        = float(single_h) / float(p2_single_h);

  glGenTextures(total, out->textures);

  SDL_Surface *single = SDL_CreateRGBSurface(SDL_SWSURFACE,
      p2_single_w, p2_single_h, 32, 
      fmt.Rmask, fmt.Gmask, fmt.Bmask, fmt.Amask);

  //SDL_Rect to_rect = {0,0,single_w, single_h};

  memset(single->pixels, 0, single->w * single-> h * 4);

  int idx = 0;
  for(int j = 0; j < count_rows; j++)
  {
    for(int i = 0; i < count_columns; i++, idx++)
    {
      SDL_Rect from_rect = { i * single_w, j * single_h, single_w, single_h };
      //memset(single->pixels, 0, single->w * single-> h * 4);
      int n;
      for(n = 0; n < single_h; n++)
      {
        uint32_t *dst = &(((uint32_t*)single->pixels)[n * p2_single_w]);
        uint32_t *src = &(((uint32_t*)nimg->pixels)[(n + from_rect.y) * nimg->w + from_rect.x]);
        memcpy(dst, src, single_w * 4);
      }

      glBindTexture(GL_TEXTURE_2D, out->textures[idx]);
      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
      
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, p2_single_w, p2_single_h,
	0, GL_RGBA, GL_UNSIGNED_BYTE, single->pixels);
    }
  }

  printf("Sprite \"%s\" loaded (%i sprites, %.1f%% transperancy, %.3fx%.3f UV fix)\n",
      file_name, 
      total, (float(tr_count) / float(px_count)) * 100.0f, out->u_max, out->v_max);
 
  SDL_FreeSurface(img);
  SDL_FreeSurface(nimg);
  SDL_FreeSurface(single);
}


void gynvael_GetMouse(float *x, float *y)
{
  int map_id = g_GameState.currentLevel->activeMap;

  float fix_x = ((float)g_GameState.currentLevel->maps[map_id]->width  / 2.0f);
  float fix_y = ((float)g_GameState.currentLevel->maps[map_id]->height / 2.0f);

  float mx = float(mouse_x - screen_width / 2);
  float my = float(mouse_y - screen_height / 2);

  float factor = 38.5f / float(screen_width);

  mx *= factor;
  my *= factor;

  mx += fix_x;
  my += fix_y;


   
  *x = mx;
  *y = my;
}

void gynvael_GetTeleportDst(int tele_id, int *dx, int *dy, int *dm)
{
  for( int map = 0; map < g_GameState.currentLevel->mapCount; map++ )
  {
    for( int y = 0; y < g_GameState.currentLevel->maps[map]->height; y++ )
    {
      for( int x = 0; x < g_GameState.currentLevel->maps[map]->width; x++ )
      {
        TILE_STRUCT *t = &g_GameState.currentLevel->maps[map]->tiles[y][x];
        if(t->tileType == TILE_TELEPORT && t->params[0] == tele_id)
        {
          *dx = x;
          *dy = y;
          *dm = map;
          return;
        }
      }
    }
  }

  *dx = -1; *dy = -1; *dm = -1;

  // Show warning only for non-player teleports
  if(tele_id != 99)
    printf("Warning: Teleport %i not found, huh\n", tele_id);
}

